import React, { ReactNode, useEffect, useState, useRef, useMemo } from 'react';
import { Select, Spin, Empty, message } from 'antd';
import type { SelectProps } from 'antd';
import { debounce, createCheckoutKey } from '../utils';
import { useParseValue } from '../utils/hooks';

interface CSelectProps<T, R> extends SelectProps {
    /* 下拉框的name（显示文字的key） */
    name?: string;
    /* 下拉框的code（提交数据的key） */
    code?: string;
    /* 多选时最大可选数量 */
    maxCount?: number;
    /* 下拉框禁用的选项 */
    disabledOption?: Array<string | number>;
    /* 被禁用选项的字段 */
    disabledField?: keyof T;
    /* 初始化的默认数据，用作数据复显（配合Form 表单的initialValue） */
    initialDefaultData?: Array<T | Record<string, string | number>>;
    /* 数据源，request 存在时，dataSource 无效 */
    dataSource?: Array<T>;
    /* 获取Select 的ref */
    createRef?: React.Ref<any>;
    /* 是否开启本地搜索，当使用request 时，本地搜索会关闭，默认返回全量的请求数据 */
    enableLocalSearch?: boolean;
    /* request 请求失败时是否提示 */
    errorHint?: boolean;
    /* request 请求的动态参数 */
    params?: Record<string, any> | (() => Record<string, any>);
    /* 当disabledField 存在时，传入handleDisabled 进行处理 */
    handleDisabled?: (value: any) => boolean;
    /* 格式化数据源 */
    formatDataSource?: (dataSource: any) => Array<T>;
    /* 获取数据源的请求方法，优先级大于dataSource */
    request?: (params?: Record<string, any>, searchValue?: string) => Promise<Array<T>>;
    /* 下拉框搜索的过滤器 */
    extendFilterOption?: (target: T) => boolean;
    /* 渲染下拉框选项的方法 */
    render?: (name: string | number, taregt: T) => ReactNode;
    /* 给下拉框选项设置的key */
    ownKey?: (target: T) => string;
    /* 处理下拉框改变的回调，初始值的设置并不会触发handleChange */
    handleChange?: (value: valueType, target: T | Array<T>, option: any) => void;
}

type valueType = string | number | Array<string | number>;

const CSelect = <T, R = any>(props: CSelectProps<T, R>) => {
    // 自定义的属性
    const {
        name = 'name',
        code = 'code',
        maxCount,
        disabledOption,
        disabledField,
        initialDefaultData,
        dataSource,
        createRef,
        enableLocalSearch,
        errorHint,
        params,
        handleDisabled,
        formatDataSource,
        extendFilterOption,
        render,
        ownKey,
        request,
        value: $1,
        onChange: $2,
        handleChange: $3,
        ...other
    } = props;
    // Select 本身的属性
    const { value, mode } = props;

    const [list, setList] = useState<Array<T>>([]);
    const [loading, setLoading] = useState(false);
    const [selectValue, setSelectValue] = useState<valueType | undefined>(undefined);
    const defaultDataSource = useRef<Array<T>>([]);
    const parsedValue = useParseValue(value);

    useEffect(() => {
        if (value === undefined) return;

        const initValue = parseValue(value, code);
        setSelectValue(initValue);
    }, [parsedValue]);

    useEffect(() => {
        getDataSource(getParams())?.then((dataList: any) => {
            changeList(dataList);
        });
    }, [JSON.stringify(params)]);

    /*
     * 作为优化手段，直接传入静态的dataSource 每次父组件重渲染都会导致传入的数据源引用改变，副作用会一直执行，
     * （* 建议父组件传入的静态数据源采用useMemo 缓存或者提到组件外 *）
     *
     * 采用useMemo 进行优化（虽然useMemo 也会不断执行，但是可避免更多不必要的操作）
     * 传入request 时，设置的dataSource 优先级不如request，直接忽略
     */
    const dataSourceIsChanged = useMemo(() => {
        if (request) return false;

        return dataSource?.reduce((pre, item: any) => `${item[code]},${pre}`, '');
    }, [dataSource]);

    useEffect(() => {
        if (request || !dataSource) return;
        const dataList = formatDataSource ? formatDataSource(dataSource) : dataSource;

        if (initialDefaultData) {
            const mergeDataList = mergeInitialData(initialDefaultData, dataList);
            changeList(mergeDataList);
        } else {
            changeList(dataList);
        }
    }, [dataSourceIsChanged]);

    const notFound = useMemo(
        () => (
            <Spin
                spinning={loading}
                size='small'
            >
                <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
            </Spin>
        ),
        [loading]
    );

    const style = useMemo(
        () => ({
            minWidth: 140,
            width: '100%',
        }),
        []
    );

    const changeList = (dataList: Array<T>) => {
        setList(dataList);
        defaultDataSource.current = dataList;
    };

    const getParams = () => {
        return typeof params === 'function' ? params() : params;
    };

    const getDataSource = (
        params?: Record<string, any>,
        isOnSearch?: boolean,
        searchValue?: string
    ) => {
        if (!request) return;
        setLoading(true);
        setList([]);

        return request(params, searchValue)
            .then((data) => {
                if (formatDataSource) {
                    data = formatDataSource(data);
                }

                if (initialDefaultData && !isOnSearch) {
                    const mergeDataList = mergeInitialData(initialDefaultData, data);
                    return mergeDataList;
                }

                setList(data);
                return data;
            })
            .catch((err) => {
                errorHint && message.error(err || '请求失败！');
            })
            .finally(() => {
                setLoading(false);
            });
    };

    const parseValue = (value: valueType, code: string) => {
        if (!Array.isArray(value)) return value;

        return value.map((item) => {
            const value = typeof item === 'object' ? item[code] : item;

            return valueToStringForTags(value);
        });
    };

    const valueToStringForTags = (value: string | number) => {
        return mode === 'tags' ? `${value}` : value;
    };

    const getCurrentOption = (option: any, values: Array<string | number>) => {
        if (!Array.isArray(option)) return option?.__tag;

        return option.map((item: any, index) => {
            const { __tag: currentTarget } = item;

            return currentTarget !== undefined
                ? currentTarget
                : {
                      [code]: values[index],
                      [name]: values[index],
                  };
        });
    };

    const mergeInitialData = (
        initialData: Array<T | Record<string, string | number>>,
        data: Array<T>
    ) => {
        const [_, hasKey] = createCheckoutKey(data, (item: any) =>
            valueToStringForTags(item[code])
        );

        const newDataList = initialData.reduce((pre, item: any) => {
            const key = item[code];
            if (!hasKey(key)) {
                return [
                    {
                        ...item,
                        [code]: valueToStringForTags(key),
                    },
                ].concat(pre);
            }

            return pre;
        }, data);

        setList(newDataList);
        return newDataList;
    };

    const handleFilterOption = (inputValue: string, option: any) => {
        if (request && !enableLocalSearch && mode !== 'tags') return true;

        const __tag = option?.__tag;
        if (!__tag) return false;

        if (extendFilterOption) return extendFilterOption(__tag);
        const target = __tag[name] + __tag[code];

        return target.includes(inputValue);
    };

    const handleSearch = (searchValue?: string) => {
        // tags模式，搜索时不进行网络请求，支持本地搜索筛选
        if (mode === 'tags') return;

        getDataSource(getParams(), true, searchValue?.trim());
    };

    const handleChange = (value: valueType, option: any) => {
        if (Array.isArray(value) && maxCount && value.length > maxCount) {
            setSelectValue(value.slice(0, maxCount));
            message.warning(`所选项不能超过 ${maxCount} 项！`);
            return;
        }

        if (props.onChange) {
            props.onChange(value, option);
        }

        if (props.handleChange) {
            props.handleChange(
                value,
                getCurrentOption(option, value as Array<string | number>),
                option
            );
        }

        setSelectValue(value);
    };

    const handleDropdownVisibleChange = (open: boolean) => {
        if (!open || !request) return;

        setList(defaultDataSource.current);
    };

    return (
        <Select
            allowClear
            showSearch
            ref={createRef}
            value={selectValue}
            placeholder={mode === 'multiple' ? '请选择（多选）' : '请选择'}
            notFoundContent={notFound}
            style={style}
            filterOption={handleFilterOption}
            onSearch={debounce(handleSearch)}
            onChange={handleChange}
            onDropdownVisibleChange={handleDropdownVisibleChange}
            {...other}
        >
            {list.map((target: any) => {
                const _code = valueToStringForTags(target[code]);
                const _name = target[name];
                const key = ownKey ? ownKey(target) : _code || Date.now();
                const disabled =
                    disabledOption?.includes(_code) ||
                    (disabledField &&
                        (handleDisabled
                            ? handleDisabled(target[disabledField])
                            : target[disabledField]));

                return (
                    <Select.Option
                        key={key}
                        value={_code}
                        title={_name}
                        __tag={target}
                        disabled={disabled}
                    >
                        {render ? render(_name, target) : _name}
                    </Select.Option>
                );
            })}
        </Select>
    );
};

export default CSelect;
